//
// Created by huafv on 8/20/24.
//

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/ide.h>


#define LED_MAJOR_NUM 200
#define LED_NAME "my_led"
// #define DEVICE_PATH "/dev/my_led"

#define LEDOFF 	0				/* led off */
#define LEDON 	1				/* led on */

// driver raw reg
#define CCM_CCGR1_BASE				(0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE		(0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE		(0X020E02F4)
#define GPIO1_DR_BASE				(0X0209C000)
#define GPIO1_GDIR_BASE				(0X0209C004)


// driver io mem
static void __iomem *CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

static int led_open (struct inode *inode, struct file *filp);
static ssize_t led_read (struct file *filp, char __user *buf, size_t cnt, loff_t *offt);
static ssize_t led_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *offt);
static int led_release (struct inode * inode, struct file * filp);

void led_switch(u8 sta);

static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release
};

static int __init my_led_init(void) {
    int ret = 0;
    u32 val;

    /* 1、hw_reg to io_mem */
    CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

    /* 2、enable GPIO1 clock */
    val = readl(CCM_CCGR1);
    val &= ~(3 << 26);	/* clear old setting */
    val |= (3 << 26);	/* set newval */
    writel(val, CCM_CCGR1);

    /* 3、set GPIO1_IO03 mux，mux set
     *    GPIO1_IO03，set io attr。
     */
    writel(5, SW_MUX_GPIO1_IO03);

    /*reg SW_PAD_GPIO1_IO03 set io attr
     *bit 16:0 HYS关闭
     *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
     */
    writel(0x10B0, SW_PAD_GPIO1_IO03);

    /* 4、set GPIO1_IO03 output */
    val = readl(GPIO1_GDIR);
    val &= ~(1 << 3);	/* clear old setting */
    val |= (1 << 3);	/* set output */
    writel(val, GPIO1_GDIR);

    /* 5、default led off  */
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);

    /* register led dev */
    ret = register_chrdev(LED_MAJOR_NUM, LED_NAME, &led_fops);
    if(ret < 0) {
        printk(KERN_ALERT "my_led: failed to register the character device\n");
    }else {
        printk(KERN_ALERT "my_led: character device registered\n");
    }
    return 0;
}
static void __exit my_led_exit(void) {
    iounmap(CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    unregister_chrdev(LED_MAJOR_NUM, LED_NAME);

}

static int led_open (struct inode *inode, struct file *filp) {
    return 0;
}
static ssize_t led_read (struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {
    return 0;
}
static ssize_t led_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {
    int ret = 0;
    char led_status = 0;
    char data_buf[1] = {0};

    ret = copy_from_user(data_buf, buf, sizeof(data_buf));
    if(ret < 0) {
        printk(KERN_ALERT "my_led: copy_from_user failed\n");
        return -EFAULT;
    }

    led_status = data_buf[0];

    printk(KERN_ALERT "my_led: led_status = %d\n", led_status);

    if(led_status == LEDON) {
        led_switch(LEDON);
    }else if (led_status == LEDOFF) {
        led_switch(LEDOFF);
    }


    return 0;
}
static int led_release (struct inode * inode, struct file * filp) {
    return 0;
}


void led_switch(u8 sta)
{
    u32 val = 0;
    if(sta == LEDON) {
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);
    }else if(sta == LEDOFF) {
        val = readl(GPIO1_DR);
        val|= (1 << 3);
        writel(val, GPIO1_DR);
    }
}

module_init(my_led_init);
module_exit(my_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("huafv");
